#ifndef _STATEMACHINE_H_
#define _STATEMACHINE_H_

#define STATES_MACHINE_MAX_DEPTH 32

struct StateMachineMode
{
	enum {
		Constructor  = 0,
		Destructor   = 1,
		Update       = 2,
		Draw2D       = 3,
		LostFocus    = 5,
		GainFocus    = 6,
		Suspend      = 7,
		Resume       = 8,
		MaxStackSize = 9,
	};
};



class StateMachine
{

public:
	StateMachine();
	~StateMachine();

	void FreeStateMachine();
	void InitStateMachine();

	void PopState(bool bImmediatMode = true);
	void PushState(int stateToPush, bool bImmediatMode = true);
	void SwitchState(int stateToSwitchTo, bool bImmediatMode = true, bool popAllStates = false);
	void PopUntil(int stateIndex, bool bImmediatMode = true);

	void StateMachineUpdate() { UpdateState(m_sm_depth, StateMachineMode::Update); }
	void StateMachineDraw2D() { UpdateState(m_sm_depth, StateMachineMode::Draw2D); }


	inline int GetStateAtDepth(int depth) const { return ( m_sm_stack[depth] & (~(1<<31)) ); }
	inline int CurrentStateDepth() const { return m_sm_depth; }
	inline int CurrentState() const { return GetStateAtDepth(m_sm_depth); }
	inline int GetState() const { return CurrentState(); }
	inline int CurrentStateAndFocus() const { return (m_sm_stack[m_sm_depth]); }
	inline bool CurrentStateHasFocus() const { return HasFocus(CurrentStateAndFocus()); }
	inline bool HasFocus(int stateAndFocus) const { return ((stateAndFocus&(1<<31))!=0); }
	inline bool IsReady() { return m_sm_delayedCommandsSize == 0; }

	inline void GiveFocusToCurrentState() 
	{ 
		if(!CurrentStateHasFocus())
		{
			m_sm_stack[m_sm_depth] |= (1<<31); 
			UpdateState(m_sm_depth, StateMachineMode::GainFocus);
		}
	} 

	inline void RemoveFocusFromCurrentState() 
	{
		if(CurrentStateHasFocus())
		{
			m_sm_stack[m_sm_depth] &= ~(1<<31); 
			UpdateState(m_sm_depth, StateMachineMode::LostFocus);
		}
	} 

	virtual void DispatchState(int depth, int mode) = 0;
	virtual void TraceStateName(int stateid) {}

	bool m_isStackModified;
	void PrintStateStack(int depths);

protected:
	void UpdateState (int depth, int mode);
	bool m_bIsUpdatingState;

protected:
	void PopUntilEx(int popToState);
	void Pop();
	void Push(int stateToPush);

	int 		m_sm_depth;
	int         m_sm_stack[STATES_MACHINE_MAX_DEPTH];

	enum MESSAGE {
		POP_STATE = 0,
		PUSH_STATE = 1,
		POP_UNTIL = 2,
	};

	MESSAGE		m_sm_delayedCommands[STATES_MACHINE_MAX_DEPTH];
	int			m_sm_delayedCommandsParams[STATES_MACHINE_MAX_DEPTH];
	int			m_sm_delayedCommandsSize;
	int 		m_sm_delayedCommands_StackDepth;
	int         m_sm_delayedCommands_Stack[STATES_MACHINE_MAX_DEPTH];

#ifdef WIN32
	const char *m_sm_delayedCommands_CallerFileName[STATES_MACHINE_MAX_DEPTH];
	int m_sm_delayedCommands_CallerFileLine[STATES_MACHINE_MAX_DEPTH];
#endif

};



#endif //_STATEMACHINE_H_